package org.springframework.security.web.authentication;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;

import java.io.IOException;
import java.util.LinkedHashMap;

/**
 * @author Dillon
 * @date 2024/6/30
 * @slogan 致敬大师 致敬未来的你
 * @desc springSecurity 代理认证实现类
 * 通过该类找到真正执行认证的实现类进行认证
 */
public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {

	/**
	 * 日志对象
	 */
	private static final Log logger = LogFactory.getLog(DelegatingAuthenticationEntryPoint.class);

	/**
	 * 认证匹配器
	 */
	private final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints;

	/**
	 * 默认认证器
	 */
	private AuthenticationEntryPoint defaultEntryPoint;

	/**
	 * 构造函数
	 *
	 * @param entryPoints 初始化认证匹配器
	 */
	public DelegatingAuthenticationEntryPoint(LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints) {
		this.entryPoints = entryPoints;
	}

	/**
	 * 引导执行认证
	 *
	 * @param request       请求对象
	 * @param response      响应对象
	 * @param authException 认证异常
	 * @throws IOException      执行异常
	 * @throws ServletException 执行异常
	 */
	@Override
	public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
		// 获取并循环请求匹配器，查找到对应的匹配器后，执行对应的认证处理器
		for (RequestMatcher requestMatcher : this.entryPoints.keySet()) {
			logger.debug(LogMessage.format("尝试使用匹配器 %s 进行匹配", requestMatcher));
			if (requestMatcher.matches(request)) {
				AuthenticationEntryPoint entryPoint = this.entryPoints.get(requestMatcher);
				logger.debug(LogMessage.format("匹配成功，执行认证 %s", entryPoint));
				entryPoint.commence(request, response, authException);
				return;
			}
		}
		logger.debug(LogMessage.format("没有匹配到对应的认证器，使用默认认证器进行认证 %s", this.defaultEntryPoint));
		// 默认认证器进行认证 一般会设置一个父认证器 就是该认证处理器
		this.defaultEntryPoint.commence(request, response, authException);
	}

	/**
	 * 设置默认的认证器
	 *
	 * @param defaultEntryPoint 默认认证器
	 */
	public void setDefaultEntryPoint(AuthenticationEntryPoint defaultEntryPoint) {
		this.defaultEntryPoint = defaultEntryPoint;
	}

	@Override
	public void afterPropertiesSet() {
		Assert.notEmpty(this.entryPoints, "认证匹配器不能为空");
		Assert.notNull(this.defaultEntryPoint, "默认认证器不能为空");
	}

}
